﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using Aga.GestionDeArchivos.Domain.Entities;
using System.Data.Objects;
using System.Transactions;

namespace Aga.GestionDeArchivos.Storage
{
    public class EstanteriaRepository : RepositoryBase<Estanteria>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="EstanteriaRepository"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        public EstanteriaRepository(AgaEntities context)
            : base(context) { }

        /// <summary>
        /// Adds to context.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <returns></returns>
        protected override Action<Estanteria> AddToContext(Estanteria entity)
        {
            return delegate { Context.AddToEstanterias(entity); };
        }

        /// <summary>
        /// Gets the entity set.
        /// </summary>
        /// <value>The entity set.</value>
        protected override ObjectSet<Estanteria> EntitySet
        {
            get { return Context.Estanterias; }
        }

        /// <summary>
        /// Adds the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public Estanteria Add(Estanteria entity)
        {
            int numeroCaja = UltimaCaja(entity.DepositoId);

            // Genero las ubicaciones
            for (int h = 1; h <= entity.Horizontal; h++)
                for (int v = 1; v <= entity.Vertical; v++)
                    for (int f = 1; f <= entity.Fondo; f++)
                    {
                        numeroCaja++;

                        Ubicacion ubicacion = new Ubicacion();
                        ubicacion.Estanteria = entity;
                        ubicacion.Sector = entity.Sector;
                        ubicacion.Horizontal = h;
                        ubicacion.Vertical = v;
                        ubicacion.Fondo = f;
                        ubicacion.Cuadrante = v;
                        ubicacion.NumeroCaja = numeroCaja;
                        ubicacion.ModificationDate = ubicacion.CreationDate = DateTime.Now;
                        ubicacion.ModificationUser = ubicacion.CreationUser = GetCurrentUser();
                        ubicacion.CodigoBarra = String.Format("{0}-{1}-{2}-{3}-{4}-{5}-{6}", entity.Deposito.Numero, entity.Numero, entity.Sector, v, v, h, f);

                        entity.Ubicaciones.Add(ubicacion);
                    }

            base.Save(entity);

            return entity;
        }

        /// <summary>
        /// Updates the specified estanteria
        /// </summary>
        /// <param name="estanteriaId">The estanteria id.</param>
        /// <param name="horizontal">The horizontal.</param>
        /// <param name="vertical">The vertical.</param>
        /// <param name="fondo">The fondo.</param>
        /// <returns></returns>
        public Estanteria Update(int estanteriaId, int horizontal, int vertical, int fondo)
        {
            var entity = GetById(estanteriaId);

            if (entity != null)
            {
                // Elimino Filas
                if (entity.Horizontal > horizontal)
                {
                    EliminarHorizontales(entity, horizontal);
                    entity.Horizontal = horizontal;
                    Save(entity);
                }
                if (entity.Vertical > vertical)
                {
                    EliminarVerticales(entity, vertical);
                    entity.Vertical = vertical;
                    Save(entity);
                }
                if (entity.Fondo > fondo)
                {
                    EliminarFondos(entity, fondo);
                    entity.Fondo = fondo;
                    Save(entity);
                }

                // Agrego Filas
                if (entity.Horizontal < horizontal)
                {
                    AgregarHorizontales(entity, horizontal);
                    entity.Horizontal = horizontal;
                    Save(entity);
                }
                if (entity.Vertical < vertical)
                {
                    AgregarVerticales(entity, vertical);
                    entity.Vertical = vertical;
                    Save(entity);
                }
                if (entity.Fondo < fondo)
                {
                    AgregarFondos(entity, fondo);
                    entity.Fondo = fondo;
                    Save(entity);
                }
            }
            else
                throw new ArgumentException("No se encontró la estantería con Id " + estanteriaId + ". No se puede actualizar.");

            return entity;
        }

        /// <summary>
        /// Deletes the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public override void Delete(Estanteria entity)
        {
            using (var ts = new TransactionScope())
            {
                try
                {
                    var ubicaciones = entity.Ubicaciones.ToList();

                    foreach (var u in ubicaciones)
                        Context.DeleteObject(u);

                    Context.DeleteObject(entity);

                    Context.SaveChanges();

                    ts.Complete();
                }
                catch
                {
                    throw;
                }
            }
        }

        private int UltimaCaja(int depositoId)
        {
            int numeroCaja = 0;

            var ultimaUbicacion = Context.Ubicaciones
                                         .Where(u => u.Estanteria.DepositoId.Equals(depositoId))
                                         .OrderByDescending(u => u.NumeroCaja)
                                         .FirstOrDefault();

            if (ultimaUbicacion != null)
                numeroCaja = ultimaUbicacion.NumeroCaja;
            else
            {
                numeroCaja = RepositoryFactory.CreateDepositoRepository().GetById(depositoId).UbicacionInicial - 1;
            }

            return numeroCaja;
        }

        private void EliminarHorizontales(Estanteria estanteria, int horizontal)
        {
            var ubicaciones = estanteria.Ubicaciones.Where(u => u.Horizontal > horizontal).ToList();

            foreach (var u in ubicaciones)
            {
                estanteria.Ubicaciones.Remove(u);
                Context.DeleteObject(u);
            }
        }

        private void EliminarVerticales(Estanteria estanteria, int vertical)
        {
            var ubicaciones = estanteria.Ubicaciones.Where(u => u.Vertical > vertical).ToList();

            foreach (var u in ubicaciones)
            {
                estanteria.Ubicaciones.Remove(u);
                Context.DeleteObject(u);
            }
        }

        private void EliminarFondos(Estanteria estanteria, int fondo)
        {
            var ubicaciones = estanteria.Ubicaciones.Where(u => u.Fondo > fondo).ToList();

            foreach (var u in ubicaciones)
            {
                estanteria.Ubicaciones.Remove(u);
                Context.DeleteObject(u);
            }
        }

        private void AgregarHorizontales(Estanteria entity, int horizontal)
        {
            int numeroCaja = UltimaCaja(entity.DepositoId);

            // Genero las ubicaciones
            for (int h = entity.Horizontal + 1; h <= horizontal; h++)
                for (int v = 1; v <= entity.Vertical; v++)
                    for (int f = 1; f <= entity.Fondo; f++)
                    {
                        numeroCaja++;

                        Ubicacion ubicacion = new Ubicacion();
                        ubicacion.Estanteria = entity;
                        ubicacion.Sector = entity.Sector;
                        ubicacion.Horizontal = h;
                        ubicacion.Vertical = v;
                        ubicacion.Fondo = f;
                        ubicacion.Cuadrante = v;
                        ubicacion.NumeroCaja = numeroCaja;
                        ubicacion.ModificationDate = ubicacion.CreationDate = DateTime.Now;
                        ubicacion.ModificationUser = ubicacion.CreationUser = GetCurrentUser();

                        entity.Ubicaciones.Add(ubicacion);
                    }
        }

        private void AgregarVerticales(Estanteria entity, int vertical)
        {
            int numeroCaja = UltimaCaja(entity.DepositoId);

            // Genero las ubicaciones
            for (int v = entity.Vertical + 1; v <= vertical; v++)
                for (int h = 1; h <= entity.Horizontal; h++)
                    for (int f = 1; f <= entity.Fondo; f++)
                    {
                        numeroCaja++;

                        Ubicacion ubicacion = new Ubicacion();
                        ubicacion.Estanteria = entity;
                        ubicacion.Sector = entity.Sector;
                        ubicacion.Horizontal = h;
                        ubicacion.Vertical = v;
                        ubicacion.Fondo = f;
                        ubicacion.Cuadrante = v;
                        ubicacion.NumeroCaja = numeroCaja;
                        ubicacion.ModificationDate = ubicacion.CreationDate = DateTime.Now;
                        ubicacion.ModificationUser = ubicacion.CreationUser = GetCurrentUser();

                        entity.Ubicaciones.Add(ubicacion);
                    }
        }

        private void AgregarFondos(Estanteria entity, int fondo)
        {
            int numeroCaja = UltimaCaja(entity.DepositoId);

            // Genero las ubicaciones
            for (int f = entity.Fondo + 1; f <= fondo; f++)
                for (int v = 1; v <= entity.Vertical; v++)
                    for (int h = 1; h <= entity.Horizontal; h++)
                    {
                        numeroCaja++;

                        Ubicacion ubicacion = new Ubicacion();
                        ubicacion.Estanteria = entity;
                        ubicacion.Sector = entity.Sector;
                        ubicacion.Horizontal = h;
                        ubicacion.Vertical = v;
                        ubicacion.Fondo = f;
                        ubicacion.Cuadrante = v;
                        ubicacion.NumeroCaja = numeroCaja;
                        ubicacion.ModificationDate = ubicacion.CreationDate = DateTime.Now;
                        ubicacion.ModificationUser = ubicacion.CreationUser = GetCurrentUser();

                        entity.Ubicaciones.Add(ubicacion);
                    }
        }
    }
}
